package com.sys.utils;

import com.alibaba.fastjson.JSON;
import com.bhudy.exception.SysException;
import com.bhudy.exception.SysExceptionCode;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.net.ssl.HttpsURLConnection;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author com.bhudy
 * @Description 工具类
 * @Date 2019-10-30 23:09:37
 */
public final class Utils {

    private Utils() {
    }

    private static WebApplicationContext webApplicationContext;

    public static void init(WebApplicationContext wac) {
        Utils.webApplicationContext = wac;
    }

    public static <C> C getBean(Class<C> beanClass) {
        return webApplicationContext.getBean(beanClass);
    }

    /**
     * 去除_，首字母转大写
     * 如：sys_user_info --> SysUserInfo
     */
    public static String toUpperCaseFirstOneDel(String str) {
        String[] tableNames = str.split("_");
        StringBuffer sbStr = new StringBuffer();
        for (int i = 0; i < tableNames.length; i++) {
            sbStr.append(Character.toUpperCase(tableNames[i].charAt(0)));
            sbStr.append(tableNames[i].substring(1));
        }
        return sbStr.toString();
    }

    /**
     * 去除_，首字母转小写
     * 如：sys_user_info --> sysUserInfo
     */
    public static String toLowerCaseFirstOneDel(String str) {
        String[] tableNames = str.split("_");
        StringBuffer sbStr = new StringBuffer();
        sbStr.append(Character.toLowerCase(tableNames[0].charAt(0)));
        sbStr.append(tableNames[0].substring(1));
        for (int i = 1; i < tableNames.length; i++) {
            sbStr.append(Character.toUpperCase(tableNames[i].charAt(0)));
            sbStr.append(tableNames[i].substring(1));
        }
        return sbStr.toString();
    }

    /**
     * 大写字母前面加_
     * 如：SysUserInfo --> sys_user_info
     */
    public static String toUpperCaseFirstOneAdd(String str) {
        StringBuffer sbStr = new StringBuffer();
        sbStr.append(Character.toLowerCase(str.charAt(0)));
        for (int i = 1; i < str.length(); i++) {
            if (Character.isUpperCase(str.charAt(i))) {
                sbStr.append("_");
            }
            sbStr.append(Character.toLowerCase(str.charAt(i)));
        }
        return sbStr.toString();
    }

    /**
     * md5密码加密
     *
     * @param password
     * @param salt
     * @return
     */
    public static final String md5(String password, String salt) {
        //加密方式
        String hashAlgorithmName = "MD5";
        //盐：为了即使相同的密码不同的盐加密后的结果也不同
        ByteSource byteSalt = ByteSource.Util.bytes(salt);
        //加密次数
        int hashIterations = 1024;
        SimpleHash result = new SimpleHash(hashAlgorithmName, password, byteSalt, hashIterations);
        return result.toHex();
    }

    /**
     * 获取HeadersInfo的值
     *
     * @return
     */
    public static Map<String, String> getHeadersInfo() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Map<String, String> map = new HashMap<>();
            Enumeration headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String key = (String) headerNames.nextElement();
                String value = request.getHeader(key);
                map.put(key, value);
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取HeadersAccessToken的值
     *
     * @return
     */
    public static String getAccessToken() {
        Map<String, String> headersInfoMap = getHeadersInfo();
        if (headersInfoMap == null || (headersInfoMap.containsKey("access_token") && headersInfoMap.get("access_token").equals("[object Null]") && headersInfoMap.get("access_token").equals(""))) {
            return null;
        } else if (headersInfoMap.containsKey("access_token")) {
            return headersInfoMap.get("access_token");
        }
        return null;
    }


    public static boolean isBlank(Object obj) {
        if (obj == null) return false;
        if (obj instanceof Collection) {
            return obj != null;
        } else if (obj instanceof Map) {
            return obj != null;
        } else if (obj.getClass().isArray()) {
            Object[] objs = (Object[]) obj;
            return objs != null && objs.length != 0;
        } else {
            return false;
        }
    }

    public static Integer formatInt(Object val) {
        if (val != null) return Integer.valueOf(String.valueOf(val));
        return null;
    }

    public static Long formatLong(Object val) {
        if (val != null) return Long.valueOf(String.valueOf(val));
        return null;
    }

    public static Double formatDouble(Object val) {
        if (val != null) return Double.valueOf(String.valueOf(val));
        return null;
    }

    public static Date formatDate(Object val) {
        if (val != null) {
            if (val.getClass().getSimpleName().equals("Long") || val instanceof Long || val.getClass().getSimpleName().equals("Integer") || val instanceof Integer) {
                return new Date(formatLong(val));
            } else if (val.getClass().getSimpleName().equals("Date") || val instanceof Date) {
                return (Date) val;
            } else {
                List<SimpleDateFormat> sdfList = new ArrayList<>();
                sdfList.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:write"));
                sdfList.add(new SimpleDateFormat("yyyy-MM-dd"));
                sdfList.add(new SimpleDateFormat("yyyy/MM/dd HH:mm:write"));
                sdfList.add(new SimpleDateFormat("yyyy/MM/dd"));
                sdfList.add(new SimpleDateFormat("yyyy年MM月dd日 HH:mm:write"));
                sdfList.add(new SimpleDateFormat("yyyy年MM月dd日"));

                for (SimpleDateFormat simpleDateFormat : sdfList) {
                    try {
                        return simpleDateFormat.parse(val.toString());
                    } catch (ParseException e) {

                    }
                }

                return null;
            }
        } else {
            return null;
        }
    }


    /**
     * 根据分割符处理时间，然后把时间转字符串返回
     *
     * @param val
     * @return 时间转字符串
     */
    public static String[] formatDateStr(Object val) {
        if (val != null) {
            String[] dateStrs = String.valueOf(val).split(",");
            if (dateStrs.length != 2) {
                throw new SysException(SysExceptionCode.CODE_40037);
            }

            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
            dateStrs[0] = simpleDateFormat.format(Utils.formatDate(Utils.formatLong(dateStrs[0]))) + " 00:00:00";
            dateStrs[1] = simpleDateFormat.format(Utils.formatDate(Utils.formatLong(dateStrs[1]))) + " 23:59:59";
            return dateStrs;
        } else {
            return null;
        }
    }


    /**
     * @param @param  file
     * @param @return 设定文件
     * @return String    返回类型
     * @throws
     * @Title: getFileMD5
     * @Description: (将文件md5)
     */
    public static String getFileMD5(File file) {
        if (!file.isFile()) {
            return null;
        }
        MessageDigest digest = null;
        FileInputStream in = null;
        byte buffer[] = new byte[8192];
        int len;
        try {
            digest = MessageDigest.getInstance("MD5");
            in = new FileInputStream(file);
            while ((len = in.read(buffer)) != -1) {
                digest.update(buffer, 0, len);
            }
            BigInteger bigInt = new BigInteger(1, digest.digest());
            return bigInt.toString(16);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                if (in != null) in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发送HttpPost请求
     *
     * @param strURL 服务地址
     * @param params json字符串,例如: "{ \"id\":\"12345\" }" ;其中属性名必须带双引号<br/>
     * @return 成功:返回Map
     */
    /*public static Map<String, Object> reqPostMap(String strURL, String params) {
        Map<String, Object> resultMap = JSON.parseObject(reqPost(strURL, params), Map.class);
        return resultMap;
    }*/

    /**
     * 发送HttpPost请求
     *
     * @param strURL 服务地址
     * @param params json字符串,例如: "{ \"id\":\"12345\" }" ;其中属性名必须带双引号<br/>
     * @return 成功:返回json字符串<br/>
     */
    public static Map<String, Object> reqPostMap(String strURL, String params) {
        BufferedReader reader;
        try {
            URL url = new URL(strURL);// 创建连接
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setInstanceFollowRedirects(true);
            connection.setRequestMethod("POST"); // 设置请求方式
            connection.setRequestProperty("Accept", "application/json"); // 设置接收数据的格式
            connection.setRequestProperty("Content-Type", "application/json"); // 设置发送数据的格式
            connection.connect();
            //一定要用BufferedReader 来接收响应， 使用字节来接收响应的方法是接收不到内容的
            OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "UTF-8"); // utf-8编码
            out.append(params);
            out.flush();
            out.close();
            // 读取响应
            reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            String line;
            String res = "";
            while ((line = reader.readLine()) != null) {
                res += line;
            }
            reader.close();

            return JSON.parseObject(res, Map.class);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 请求url
     *
     * @param url
     * @return
     */
    public static Map<String, Object> reqGetMap(String url) {
        try {
            URL reqURL = new URL(url);
            HttpsURLConnection openConnection = (HttpsURLConnection) reqURL.openConnection();
            openConnection.setConnectTimeout(10000);
            //这里我感觉获取openid的时间比较长，不过也可能是我网络的问题，
            //所以设置的响应时间比较长
            openConnection.connect();
            InputStream in = openConnection.getInputStream();

            StringBuilder builder = new StringBuilder();
            BufferedReader bufreader = new BufferedReader(new InputStreamReader(in));
            for (String temp = bufreader.readLine(); temp != null; temp = bufreader
                    .readLine()) {
                builder.append(temp);
            }

            String result = builder.toString();
            in.close();
            openConnection.disconnect();

            Map<String, Object> resultMap = JSON.parseObject(result, Map.class);

            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 日志
     *
     * @param clazz
     * @return
     */
    public static Logger getLogger(Class<?> clazz) {
        return LoggerFactory.getLogger(clazz);
    }

    /**
     * 日志
     *
     * @param clazz
     * @param t
     */
    public static void loggerError(Class<?> clazz, Throwable t) {
        getLogger(clazz).error(t.getMessage(), t);
    }

    public static void loggerError(Object obj, Throwable t) {
        getLogger(obj.getClass()).error(t.getMessage(), t);
    }


    /**
     * 获取用户ip
     *
     * @return
     */
    public static String getRequestIp() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (request == null) return "";
        String ip = request.getHeader("X-Requested-For");
        if ("unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if ("unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if ("unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if ("unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if ("unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if ("unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if ("0:0:0:0:0:0:0:1".equals(ip)) {
            return "127.0.0.1";
        }
        return ip;
    }


    /**
     * 计算2个时间差多毫秒，不包含周末
     *
     * @param stTime
     * @param edTime
     * @return
     */
    public static Long timeQuantum(Long stTime, Long edTime) {
        if (stTime == null || edTime == null) return null;
        long dateTime = stTime;
        long resultData = 0;
        while ((dateTime += (1000 * 60)) < edTime) {
            Date date2 = new Date(dateTime);
            if (date2.getDay() == 0 || date2.getDay() == 6) {
                resultData += 60000;
            }
        }
        return resultData;
    }


    /**
     * map转成指定list并排序
     *
     * @param map map
     * @return list
     */
    public static List<Map<String, Object>> mapToList(Map<Long, Integer> map) {
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (Map.Entry<Long, Integer> entry : map.entrySet()) {
            Map<String, Object> dataMap = new HashMap<>();
            dataMap.put("timestamp", entry.getKey());
            dataMap.put("value", entry.getValue());
            resultList.add(dataMap);
        }

        Collections.sort(resultList, (o1, o2) -> {
            Long value1 = Utils.formatLong(o1.get("timestamp"));
            Long value2 = Utils.formatLong(o2.get("timestamp"));
            return value2.compareTo(value1);
        });

        return resultList;
    }


    /**
     * 密码强度
     * 0初始 1弱 2中 3强
     * <p>
     * 一、假定密码字符数范围6-16，除英文数字和字母外的字符都视为特殊字符：
     * 弱：^[0-9A-Za-z]{6,16}$
     * 中：^(?=.{6,16})[0-9A-Za-z]*[^0-9A-Za-z][0-9A-Za-z]*$
     * 强：^(?=.{6,16})([0-9A-Za-z]*[^0-9A-Za-z][0-9A-Za-z]*){2,}$
     * 二、假定密码字符数范围6-16，密码字符允许范围为ASCII码表字符：
     * 弱：^[0-9A-Za-z]{6,16}$
     * 中：^(?=.{6,16})[0-9A-Za-z]*[\x00-\x2f\x3A-\x40\x5B-\xFF][0-9A-Za-z]*$
     * 强：^(?=.{6,16})([0-9A-Za-z]*[\x00-\x2F\x3A-\x40\x5B-\xFF][0-9A-Za-z]*){2,}$
     */
    public static int checkPasswordStrength(String passwordStr) {
        String regexZ = "\\d*";
        String regexS = "[a-zA-Z]+";
        String regexT = "\\W+$";
        String regexZT = "\\D*";
        String regexST = "[\\d\\W]*";
        String regexZS = "\\w*";
        String regexZST = "[\\w\\W]*";

        if (passwordStr.matches(regexZ)) {
            return 1;
        }
        if (passwordStr.matches(regexS)) {
            return 1;
        }
        if (passwordStr.matches(regexT)) {
            return 1;
        }
        if (passwordStr.matches(regexZT)) {
            return 2;
        }
        if (passwordStr.matches(regexST)) {
            return 2;
        }
        if (passwordStr.matches(regexZS)) {
            return 2;
        }
        if (passwordStr.matches(regexZST)) {
            return 3;
        }
        return 0;

    }

}
